package com.wiscamp.ninechapters.common.core;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.net.URL;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

public class IOHelper {
    private IOHelper() {
    }

    private static final Logger logger = LoggerFactory.getLogger(IOHelper.class);

    /**
     * check if the folder exists. if the folder does not exist, create it.
     *
     * @param fullFolderName the folder's path
     * @return return true if the folder exists
     */
    public static boolean ensureFolder(String fullFolderName) {
        var folder = new File(fullFolderName);
        if (!folder.exists()) {
            return folder.mkdirs();
        }
        return folder.exists() && folder.isDirectory();
    }

    /**
     * check if the file exists.
     *
     * @param fullFileName
     * @return
     */

    public static boolean ensureFile(String fullFileName) {
        var file = new File(fullFileName);
        return file.exists() && file.isFile();
    }

    /**
     * deletes the folder
     *
     * @param fullFolderName
     * @return
     */

    public static boolean deleteFolder(String fullFolderName) {
        var folder = new File(fullFolderName);
        if (!folder.isDirectory())
            return false;
        return deleteDirectory(folder);
    }

    /**
     * deletes the file
     *
     * @param file
     * @return
     */

    public static boolean deleteFile(File file) {
        if (file.isFile())
            return file.delete();
        return false;
    }

    /**
     * deletes the directory （循环删除子目录及文件）
     *
     * @param directory
     * @return
     */

    private static boolean deleteDirectory(File directory) {
        if (directory.isDirectory()) {
            String[] files = directory.list();
            for (String file : files) {
                var subFile = new File(directory, file);
                if (subFile.isFile()) {
                    deleteFile(subFile);
                }
                if (subFile.isDirectory()) {
                    deleteDirectory(subFile);
                }
            }
            return directory.delete();
        }
        return false;
    }

    /**
     * downloads the file from internet
     *
     * @param fileUrl
     * @param folderPath
     * @param fileName
     * @return
     */

    public static String downloadFile(String fileUrl, String folderPath, String fileName) {
        URL encodedUrl;
        var fullFileName = folderPath + fileName;
        try {
            var url = fileUrl.replace(" ", "%20");
            encodedUrl = new URL(url);
            var inputStream = new DataInputStream(encodedUrl.openStream());
            var file = new File(fullFileName);
            if (!file.exists()) {
                boolean createResult = file.createNewFile();
                if (!createResult)
                    logger.debug("Failed to create a new file.");
            }
            var fileOutputStream = new FileOutputStream(file);
            var byteArrayOutputStream = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024];
            int length;
            while ((length = inputStream.read(buffer)) > 0) {
                byteArrayOutputStream.write(buffer, 0, length);
            }
            fileOutputStream.write(byteArrayOutputStream.toByteArray());
            inputStream.close();
            fileOutputStream.close();
            Thread.sleep(10);
        } catch (Exception ex) {
            logger.error("Failed to download the file: {}", fileUrl, ex);
            fullFileName = "";
        }

        return fullFileName;
    }


    /**
     * compress file
     *
     * @param sourceFile 压缩源路径
     * @param targetFile 压缩目的路径
     */
    public static void compress(String sourceFile, String targetFile) {
        var file = new File(sourceFile);
        if (!file.exists()) {
            throw new IllegalArgumentException(sourceFile + " do not exist.");
        }

        try {
            var zipFile = new File(targetFile);
            var fileOutputStream = new FileOutputStream(zipFile);
            var zipOutputStream = new ZipOutputStream(fileOutputStream);
            var baseDirectory = "";
            compressByType(file, zipOutputStream, baseDirectory);
            zipOutputStream.close();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    /**
     * 按照原路径的类型就行压缩。文件路径直接把文件压缩，
     *
     * @param sourceFile
     * @param stream
     * @param baseFolder
     */

    private static boolean compressByType(File sourceFile, ZipOutputStream stream, String baseFolder) {
        if (!sourceFile.exists())
            return false;
        // 判断文件是否是文件，如果是文件调用compressFile方法,如果是路径，则调用compressDir方法；
        if (sourceFile.isFile()) {
            return compressFile(sourceFile, stream, baseFolder);
        } else if (sourceFile.isDirectory()) {
            return compressDirectory(sourceFile, stream, baseFolder);
        }
        return false;
    }

    /**
     * Compress a file
     */
    private static boolean compressFile(File file, ZipOutputStream stream, String baseFolder) {
        if (!file.exists())
            return false;
        try {
            var inputStream = new BufferedInputStream(new FileInputStream(file));
            var entry = new ZipEntry(baseFolder + file.getName());
            stream.putNextEntry(entry);
            int count;
            byte[] buf = new byte[1024];
            while ((count = inputStream.read(buf)) != -1) {
                stream.write(buf, 0, count);
            }
            inputStream.close();
            return true;
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return false;
    }

    /**
     * Compress a directory
     */
    private static boolean compressDirectory(File sourceDirectory, ZipOutputStream stream, String baseFolder) {
        if (!sourceDirectory.exists())
            return false;
        File[] files = sourceDirectory.listFiles();
        if (files == null) return false;
        if (files.length == 0) {
            try {
                stream.putNextEntry(new ZipEntry(baseFolder + sourceDirectory.getName() + File.separator));
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
        for (var file : files) {
            compressByType(file, stream, baseFolder + sourceDirectory.getName() + File.separator);
        }
        return true;
    }

    /**
     * Compress a folder by zip algorithm
     *
     * @param sourceFolder
     * @param targetFolder
     * @param zipFileName
     * @return
     */

    public static boolean zipFolder(String sourceFolder, String targetFolder, String zipFileName) {
        try {
            var sourceDirectory = new File(sourceFolder);
            if (!sourceDirectory.exists() || sourceDirectory.isFile())
                return false;
            ensureFolder(targetFolder);
            var zipFile = new File(targetFolder + File.separator + zipFileName); // 定义压缩文件名称
            var stream = new ZipOutputStream(new FileOutputStream(zipFile));
            boolean success = compressDirectory(sourceDirectory, stream, targetFolder);
            stream.close();
            return success;
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return false;
    }

    /**
     * Writes text to a file
     *
     * @param builder
     * @param fullFileName
     * @param isAppend
     * @return
     */

    public static boolean writeTextFile(StringBuilder builder, String fullFileName, boolean isAppend) {
        try {
            var writer = new FileWriter(fullFileName, isAppend);
            writer.write(builder.toString());
            writer.close();
            return true;
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return false;
    }

    public static boolean writeTextFile(String text, String fullFileName, boolean isAppend) {
        try {
            var writer = new FileWriter(fullFileName, isAppend);
            writer.write(text);
            writer.close();
            return true;
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return false;
    }

    /**
     * Reads text from a file
     *
     * @param fullFileName
     * @return
     */

    public static StringBuilder readTextFile(String fullFileName) {
        try {
            var textFile = new File(fullFileName);
            if (!textFile.exists())
                return null;
            if (textFile.isFile()) {
                var builder = new StringBuilder();
                var reader = new FileReader(textFile);
                var bufferedReader = new BufferedReader(reader);
                String line;
                while ((line = bufferedReader.readLine()) != null) {
                    builder.append(line);
                }
                bufferedReader.close();
                return builder;
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return null;
    }

}


